package gateway.mgt.runtime

import grails.converters.JSON
import grails.gorm.dirty.checking.DirtyCheck
import grails.validation.Validateable
import groovy.transform.ToString

@ToString(includePackage = false, includes = ["type", "host", "remoteAddress"])
class ManagedContainer {

    static enum Type {
        Apache_Karaf//, Centos7_JRE8
    }

    @DirtyCheck
    @ToString(includePackage = false, includes = ["name", "sshPort", "committedMemory"])
    static class SystemMemory implements Validateable {
        Integer total
        Integer committed = 0
        BigDecimal usage = 0 //committed memory / total memory

        static constraints = {
            total(nullable: false, min: 2)
            committed(nullable: false, min: 0, bindable: false)
            usage(nullable: false, range: 0..100, scale: 2, bindable: false)
        }
    }

    @DirtyCheck
    @ToString(includePackage = false, includes = ["name", "sshPort", "committedMemory"])
    static class AppInstance implements Validateable {
        String name
        Integer sshPort
        Byte committedMemory //Gb

        static constraints = {
            name(blank: false, unique: true)
            sshPort(nullable: false, min: 1)
            committedMemory(nullable: false, min: 1)
        }
    }

    Type type
    String description

    String hostName

    String remoteAddress
    Integer defaultSshPort = 8101 //root instance
    String username = "karaf"
    String password = "karaf"

    SystemMemory memoryUsage

    Boolean autoInstall = false
    Boolean installed = true

    Map<String, String> apps//<sshPort>:<AppInstance as JSON>

    Date dateCreated
    Date lastUpdated

    static embedded = ['memoryUsage']

    static constraints = {
        type(nullable: false)
        hostName(blank: false, unique: true, matches: /[a-zA-Z0-9]+/)
        memoryUsage(nullable: false)
        remoteAddress(blank: false, unique: true, matches: /(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))/)
        defaultSshPort(nullable: false, min: 1)
        username(blank: false)
        password(blank: false)
        autoInstall(nullable: false)
        installed(nullable: false, bindable: false)
        description(nullable: true, maxSize: 100, widget: 'textarea')
        apps()
    }

    static ManagedContainer choose() {
        ManagedContainer.withCriteria(uniqueResult: true) {
            memoryUsage {
                ge("usage", 70)
            }
            order("usage", "desc")
        }
    }

    private static void refreshHosts() {
        def hosts = new File("hosts").newWriter()

        hosts << ManagedContainer.all.collect { container ->
            "$container.hostName $container.remoteAddress" as String
        }.join("\n")

        hosts.close()
    }

    def afterInsert() {
        refreshHosts()
    }

    def afterUpdate() {
        refreshHosts()
    }

    def afterDelete() {
        refreshHosts()
    }

    boolean deploy(String coreName) {
        setup(coreName)
        def sshPort = JSON.parse(apps[coreName])["sshPort"]
        //TODO automated deploy core.jar via ansible playbook(deployWar.yml)
        def extraParams = ([
                karaf_hostname: hostName, karaf_ssh_port: sshPort, webbundle_name: "GatewayCore:$hostName", webbundle_contextPath: "/sample",
                webbundle_url : "http://tomcat.apache.org/tomcat-8.5-doc/appdev/sample/sample.war"
        ] as JSON) as String

        exec("ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i hosts deployWar.yml -e '$extraParams'")
        return false
    }

    private void setup(String coreName) {
        if (!installed && autoInstall)
            install()

        def app = new AppInstance(name: coreName, sshPort: defaultSshPort + apps.size() + 1, committedMemory: 1)
        //TODO automated install karaf instance via ansible playbook(setupApp.yml and addFeatures.yml)
        def extraParams1 = ([
                karaf_hostname: hostName, karaf_ssh_port: defaultSshPort, app_name: coreName, app_ssh_port: app.sshPort,
                app_jvm_opts  : "-Xms512m -Xmx512m --spring.application.name=$coreName"
        ] as JSON) as String

        exec("ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i hosts setupApp.yml -e '$extraParams1'")

        def extraParams2 = ([
                karaf_hostname: hostName, karaf_ssh_port: app.sshPort, features: "war"
        ] as JSON) as String

        exec("ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i hosts addFeatures.yml -e '$extraParams2'")

        apps[app.name] = ((app as JSON) as String)

        save(flush: true)
        refresh()
    }

    private def install() {
        //TODO automated install container via ansible playbook
        //exec("wmic cpu get processorid")
    }

    private String exec(String command) {
        println "[Execute, ${new Date().format('yyyy-MM-dd HH:mm:ss,S')}] $command"
        def proc = command.execute()
        proc.outputStream.close()
        proc.waitFor()
        return proc.inputStream.text
    }
}
